package com.teradata.olingo.processor.core.processor;

import com.teradata.olingo.processor.core.api.GenericODataRequestContextAccess;
import com.teradata.olingo.processor.core.api.GenericODataSessionContextAccess;
import com.teradata.olingo.processor.core.exception.ODataGenericProcessorException;
import com.teradata.olingo.processor.core.modify.GenericConversionHelper;
import com.teradata.olingo.processor.core.serializer.GenericSerializerFactory;
import org.apache.olingo.commons.api.ex.ODataException;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceKind;

import java.util.List;

public final class GenericProcessorFactory {
  private final GenericODataSessionContextAccess sessionContext;
  private final GenericSerializerFactory serializerFactory;
  private final OData odata;
  private final ServiceMetadata serviceMetadata;

  public GenericProcessorFactory(final OData odata, final ServiceMetadata serviceMetadata,
                                 final GenericODataSessionContextAccess context) {
    super();
    this.sessionContext = context;
    this.serializerFactory = new GenericSerializerFactory(odata, serviceMetadata);
    this.odata = odata;
    this.serviceMetadata = serviceMetadata;
  }

  public GenericCUDRequestProcessor createCUDRequestProcessor(final UriInfo uriInfo,
                                                              final ContentType responseFormat) throws ODataException {

    final GenericODataRequestContextAccess requestContext = new GenericRequestContext(uriInfo, serializerFactory
        .createCUDSerializer(responseFormat, uriInfo));

    return new GenericCUDRequestProcessor(odata, serviceMetadata, sessionContext, requestContext,
        new GenericConversionHelper());
  }

  public GenericCUDRequestProcessor createCUDRequestProcessor(UriInfo uriInfo) throws ODataException {

    final GenericODataRequestContextAccess requestContext = new GenericRequestContext(uriInfo, null);

    return new GenericCUDRequestProcessor(odata, serviceMetadata, sessionContext, requestContext,
        new GenericConversionHelper());
  }

  public GenericActionRequestProcessor createActionProcessor(final UriInfo uriInfo,
                                                             final ContentType responseFormat) throws ODataException {

    final GenericODataRequestContextAccess requestContext = new GenericRequestContext(uriInfo,
        responseFormat != null ? serializerFactory.createSerializer(responseFormat, uriInfo) : null);

    return new GenericActionRequestProcessor(odata, sessionContext, requestContext);

  }

  public GenericRequestProcessor createProcessor(final UriInfo uriInfo,
                                                 final ContentType responseFormat) throws ODataException {

    final List<UriResource> resourceParts = uriInfo.getUriResourceParts();
    final UriResource lastItem = resourceParts.get(resourceParts.size() - 1);
    final GenericODataRequestContextAccess requestContext = new GenericRequestContext(uriInfo, serializerFactory
        .createSerializer(responseFormat, uriInfo));

    switch (lastItem.getKind()) {
    case count:
      return new GenericCountRequestProcessor(odata, sessionContext, requestContext);
    case function:
      checkFunctionPathSupported(resourceParts);
      return new GenericFunctionRequestProcessor(odata, sessionContext, requestContext);
    case complexProperty:
    case primitiveProperty:
    case navigationProperty:
    case entitySet:
    case value:
      checkNavigationPathSupported(resourceParts);
      return new GenericNavigationRequestProcessor(odata, serviceMetadata, sessionContext, requestContext);
    default:
      throw new ODataGenericProcessorException(ODataGenericProcessorException.MessageKeys.NOT_SUPPORTED_RESOURCE_TYPE,
          HttpStatusCode.NOT_IMPLEMENTED, lastItem.getKind().toString());
    }
  }

  private void checkFunctionPathSupported(final List<UriResource> resourceParts) throws ODataApplicationException {
    if (resourceParts.size() > 2)
      throw new ODataGenericProcessorException(ODataGenericProcessorException.MessageKeys.NOT_SUPPORTED_FUNC_WITH_NAVI,
          HttpStatusCode.NOT_IMPLEMENTED);
  }

  private void checkNavigationPathSupported(final List<UriResource> resourceParts) throws ODataApplicationException {
    for (final UriResource resourceItem : resourceParts) {
      if (resourceItem.getKind() != UriResourceKind.complexProperty
          && resourceItem.getKind() != UriResourceKind.primitiveProperty
          && resourceItem.getKind() != UriResourceKind.navigationProperty
          && resourceItem.getKind() != UriResourceKind.entitySet
          && resourceItem.getKind() != UriResourceKind.value)
        throw new ODataGenericProcessorException(ODataGenericProcessorException.MessageKeys.NOT_SUPPORTED_RESOURCE_TYPE,
            HttpStatusCode.NOT_IMPLEMENTED, resourceItem.getKind().toString());
    }

  }
}
